/* Foley C# Utilities
 * Copyright (C)2002 Rodney S. Foley
 *  
 * This program is free software; you can 
 * redistribute it and/or modify it under the 
 * terms of the GNU General Public License as 
 * published by the Free Software Foundation; 
 * either version 2 of the License, or (at your 
 * option) any later version.
 * 
 * This program is distributed in the hope that 
 * it will be useful, but WITHOUT ANY WARRANTY; 
 * without even the implied warranty of 
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR 
 * PURPOSE. See the GNU General Public License 
 * for more details.
 * 
 * You should have received a copy of the GNU 
 * General Public License along with this 
 * program; if not, write to: 
 * 
 *		Free Software Foundation, Inc., 
 *		59 Temple Place, Suite 330, 
 *		Boston, MA 02111-1307 USA
 * 
 *					- or -
 * 
 * http://opensource.org/licenses/gpl-license.php
 */

using System;
using System.Collections;

namespace Foley.Utilities.Collections
{
	/// <summary>
	/// Provides an abstract implementation of the <see cref="IList2"/> 
	/// interface and extends <see cref="Collection2Base"/>.
	/// </summary>	
	/// <remarks>
	/// The main purpose of this is an all purpose base class that can be 
	/// extended by any class that wants to be a list.
	/// </remarks>
	public abstract class List2Base : Collection2Base, IList2
	{
		#region Protected Constructor
		/// <summary>
		/// Protected constructor for abstract class.
		/// </summary>
		protected List2Base() {}
		#endregion

		#region Public Member Methods
		/// <summary>
		/// Gets or sets the <see cref="object"/> at the specified 
		/// <paramref name="index"/>. In C#, this property is the indexer for 
		/// the list.
		/// </summary>
		/// <param name="index">
		/// The zero-based index of the element to get or set.
		/// </param>
		/// <value>
		/// The element at the specified index.
		/// </value>
		public abstract object this[int index] {get; set;}

		/// <summary>
		/// When implemented by a class, adds an <see cref="object"/> to the list.
		/// </summary>
		/// <param name="value">
		/// The <see cref="object"/> to be added to the end of the list.
		/// </param>
		/// <returns>
		/// The zero-based index at which the <see cref="object"/> was added.
		/// </returns>
		public abstract int Add(object value);

		/// <summary>
		/// Adds the elements of the specified <see cref="ICollection"/> to the 
		/// list.
		/// </summary>
		/// <param name="collection">
		/// The <see cref="ICollection"/> whose elements are added to the list.
		/// </param>
		/// <returns>
		/// <c>true</c> if the list is modified; otherwise <c>false</c>.
		/// </returns>
		/// <exception cref="NullReferenceException">
		/// The <paramref name="collection"/> is a <c>null</c> reference 
		/// (<c>Nothing</c> in Visual Basic).
		/// </exception>
		public virtual bool Add(ICollection collection)
		{
			bool beenModified = false;

			foreach (object value in collection)
				beenModified |= (0 <= Add(value));

			return beenModified;
		}


		/// <summary>
		/// Determines whether the specified <see cref="object"/> is in the list.
		/// </summary>
		/// <param name="value">
		/// The <see cref="object"/> to check if it is contained in the list.
		/// </param>
		/// <returns>
		/// <c><c>true</c></c> if the <see cref="object"/> is found in the list; 
		/// otherwise, <c>false</c>.
		/// </returns>
		/// <remarks>
		/// This method determines equality by calling <see cref="object.Equals"/>.
		/// Keep in mind that the <see cref="object"/> to locate can be a 
		/// <c>null</c> reference (<c>Nothing</c> in Visual Basic).  If you do 
		/// not want this behaviour you must override this method.
		/// </remarks>
		public virtual bool Contains(object value)
		{
			bool contains = false;

			if (value == null)
			{
				foreach (object obj in this)
					if (obj == null)
					{
						contains = true;
						break;
					}
			}
			else
			{
				foreach (object obj in this)
					if (obj.Equals(value))
					{
						contains = true;
						break;
					}
			}

			return contains;
		}

		/// <summary>
		/// Determines whether all the elements in the specified <see cref="ICollection"/> 
		/// are contained in the list.
		/// </summary>
		/// <param name="collection">
		/// The <see cref="ICollection"/> whose elements are to be checked if 
		/// all of its elements are contained in the list.
		/// </param>
		/// <returns>
		/// <c>true</c> if all the elements of the <see cref="ICollection"/> are
		/// found in the list; otherwise, <c>false</c>.
		/// </returns>
		/// <exception cref="NullReferenceException">
		/// The <paramref name="collection"/> is a <c>null</c> reference 
		/// (<c>Nothing</c> in Visual Basic).
		/// </exception>
		/// <remarks>
		/// This method determines equality by calling <see cref="object.Equals"/>.
		/// Keep in mind that the <see cref="object"/> to locate can be a 
		/// <c>null</c> reference (<c>Nothing</c> in Visual Basic).  If you do 
		/// not want this behaviour you must override this method.
		/// </remarks>
		public virtual bool Contains(ICollection collection)
		{
			bool containsAll = true;

			foreach (object value in this)
			{
				if (!Contains(value))
				{
					containsAll = false;
					break;
				}
			}

			return containsAll;
		}		
		

		/// <summary>
		/// When implemented by a class, returns an <see cref="IEnumerator"/> 
		/// that can iterate through the list that starts at the specified 
		/// position.
		/// </summary>
		/// <param name="index">
		/// Starting <paramref name="index"/> in the list for the begining of 
		/// the <see cref="IEnumerator"/>.
		/// </param>
		/// <returns>
		/// An <see cref="IEnumerator"/> that can be used to iterate 
		/// through the list.
		/// </returns>
		public abstract IEnumerator GetEnumerator(int index);

		/// <summary>
		/// When implemented by a class, searches for the specified <see cref="object"/> 
		/// and returns the zero-based index of the first occurrence within the 
		/// list.
		/// </summary>
		/// <param name="value">
		/// The <see cref="object"/> to locate in the list.
		/// </param>
		/// <returns>
		/// The zero-based index of the first occurrence of <see cref="object"/> 
		/// within the list, if found; otherwise, -1.
		/// </returns>
		public abstract int IndexOf(object value);

		/// <summary>
		/// When implemented by a class, inserts the <see cref="object"/> 
		/// specified into the list at the specified position.
		/// </summary>
		/// <param name="index">
		/// The zero-based <paramref name="index"/> at which the 
		/// <see cref="object"/> should be inserted.
		/// </param>
		/// <param name="value">
		/// The <see cref="object"/> to insert into the list. 
		/// </param>
		/// <returns>
		/// <c>true</c> if the list is modified; otherwise <c>false</c>.
		/// </returns>
		/// <remarks>
		/// This method hides the <see cref="IList.Insert"/> method.  It is 
		/// recommened that you do an <i>explicit interface method implementation</i>
		/// for complete compatibility.
		/// </remarks>
		/// <exception cref="NotSupportedException">
		/// <para>The list is read-only.</para>
		/// -or-
		/// <para>The list has a fixed size.</para>
		/// </exception>
		public abstract bool Insert(int index, object value);
		
		/// <summary>
		/// This method provided for backwards compatiblity with IList.
		/// </summary>
		void IList.Insert(int index, object value)
		{
			Insert(index, value);
		}
		
		/// <summary>
		/// When implemented by a class, inserts the elements of the 
		/// <see cref="ICollection"/> to the list starting at the specified
		/// position.
		/// </summary>
		/// <param name="index">
		/// The zero-based <paramref name="index"/> at which collection should 
		/// be inserted.
		/// </param>
		/// <param name="collection">
		/// The <see cref="ICollection"/> whose elements are to be added to 
		/// the list.
		/// </param>
		/// <returns>
		/// <c>true</c> if the list is modified; otherwise <c>false</c>.
		/// </returns>
		/// <exception cref="NotSupportedException">
		/// <para>The list is read-only.</para>
		/// -or-
		/// <para>The list has a fixed size.</para>
		/// </exception>
		public abstract bool Insert(int index, ICollection collection);
		
		/// <summary>
		/// When implemented by a class, determines the <paramref name="index"/> 
		/// of the last occurrence of the specified <see cref="object"/> in the 
		/// list.
		/// </summary>
		/// <param name="value">
		/// The <see cref="object"/> to locate in the list.
		/// </param>
		/// <returns>
		/// The zero-based index of the last occurrence of the value in the list, 
		/// if found; otherwise, -1.
		/// </returns>
		public abstract int LastIndexOf(object value);

		/// <summary>
		/// When implemented by a class, removes the first occurrence of a 
		/// specific <see cref="object"/> from the list.
		/// </summary>
		/// <param name="value">
		/// The <see cref="object"/> to remove from the list.
		/// </param>
		/// <returns>
		/// <c>true</c> if the list is modified; otherwise <c>false</c>.
		/// </returns>
		public abstract bool Remove(object value);

		/// <summary>
		/// This method provided for backwards compatiblity with <see cref="IList"/>.
		/// </summary>
		void IList.Remove(object value)
		{
			Remove(value);
		}

		/// <summary>
		/// When implemented by a class, removes the specified
		/// number of the elements starting at the position specified.
		/// </summary>
		/// <param name="index">
		/// Starting <paramref name="index"/> for removing elements from the
		/// list.
		/// </param>
		/// <param name="count">
		/// The number of elements to remove from the list.
		/// </param>
		/// <returns>
		/// <c>true</c> if the list is modified; otherwise <c>false</c>.
		/// </returns>
		/// <exception cref="NotSupportedException">
		/// <para>The list is read-only.</para>
		/// -or-
		/// <para>The list has a fixed size.</para>
		/// </exception>
		public abstract bool Remove(int index, int count);
		
		/// <summary>
		/// Removes the first occurrence of each element of the specified 
		/// <see cref="ICollection"/> from the list.
		/// </summary>
		/// <param name="collection">
		/// The <see cref="ICollection"/> used to determine what 
		/// elements to remove from the list.
		/// </param>
		/// <returns>
		/// <c>true</c> if the list is modified; otherwise <c>false</c>.
		/// </returns>
		/// <exception cref="NotSupportedException">
		/// <para>The list is read-only.</para>
		/// -or-
		/// <para>The list has a fixed size.</para>
		/// </exception>
		public virtual bool Remove(ICollection collection)
		{
			bool modified = false;

			foreach (object value in collection)
				modified |= Remove(value);

			return modified;
		}

		/// <summary>
		/// When implemented by a class, removes the element at the specified 
		/// position from the list.
		/// </summary>
		/// <param name="index">
		/// The <paramref name="index"/> of the element to remove from the 
		/// list.
		/// </param>
		/// <returns>
		/// <c>true</c> if the list is modified; otherwise <c>false</c>.
		/// </returns>
		/// <exception cref="NotSupportedException">
		/// <para>The list is read-only.</para>
		/// -or-
		/// <para>The list has a fixed size.</para>
		/// </exception>
		public abstract bool RemoveAt(int index);
		
		/// <summary>
		/// This method provided for backwards compatiblity with <see cref="IList"/>.
		/// </summary>
		void IList.RemoveAt(int index)
		{
			Remove(index);
		}

		/// <summary>
		/// Retains the first
		/// occurrence of each element of the specified
		/// <see cref="ICollection"/> from the list.
		/// </summary>
		/// <param name="collection">
		/// The <see cref="ICollection"/> used to determine what 
		/// elements to be retained in the list.
		/// </param>
		/// <returns>
		/// <c>true</c> if the list is modified; otherwise <c>false</c>.
		/// </returns>
		/// <exception cref="NotSupportedException">
		/// <para>The list is read-only.</para>
		/// -or-
		/// <para>The list has a fixed size.</para>
		/// </exception>
		public virtual bool Retain(ICollection collection)
		{	
			bool modified = false;

			LinkedSet objectsToRemove = new LinkedSet();

			foreach (object value in this)
			{
				if (!IsContainedIn(collection, value))
					objectsToRemove.Add(value);					
			}

			foreach (object value in objectsToRemove)
				Remove(value);				
			
			if (0 < objectsToRemove.Count)
				modified = true;

			return modified;
		}	

		/// <summary>
		/// When implemented by a class, provides a view of
		/// the list with the specified number of the elements 
		/// starting at the position specified.
		/// </summary>
		/// <param name="index">
		/// Starting <paramref name="index"/> for the view of elements from
		/// the list.
		/// </param>
		/// <param name="count">
		/// The number of elements to include in the view of the list.
		/// </param>
		/// <returns>
		/// A view of list.
		/// </returns>
		/// <remarks>
		/// The list that is returned is just a view, it is still backed
		/// by the orignal list.  Any changes you make to it will be 
		/// reflected in the orignal list.
		/// </remarks>
		public abstract IList SubList(int index, int count);
		#endregion
	}
}
